#define AE_USEMEMORYMANAGER
#define AE_OPENGL

#include <AEEngine.h>

#include <Windows.h>
#include <stdio.h>

#define MAX_ATTRIBUTE_NAME_LEN	32
typedef struct _VSAttributes_tag_
{
	AEINT32	nType;
	AEINT32	nPos;
	char	szName[ MAX_ATTRIBUTE_NAME_LEN + 1 ];
}VSAttributes, *lpVSAttributes;

const char *GL_type_to_string( GLenum glType ) 
{
	switch( glType ) 
	{
		case GL_BOOL: return "bool";
		case GL_INT: return "int";
		case GL_FLOAT: return "float";
		case GL_FLOAT_VEC2: return "vec2";
		case GL_FLOAT_VEC3: return "vec3";
		case GL_FLOAT_VEC4: return "vec4";
		case GL_FLOAT_MAT2: return "mat2";
		case GL_FLOAT_MAT3: return "mat3";
		case GL_FLOAT_MAT4: return "mat4";
		case GL_SAMPLER_2D: return "sampler2D";
		case GL_SAMPLER_3D: return "sampler3D";
		case GL_SAMPLER_CUBE: return "samplerCube";
		case GL_SAMPLER_2D_SHADOW: return "sampler2DShadow";
		default: break;
	}
	return "other";
}

int main( int argc, char *argv[] )
{
	AEEngineConfig ecConfig;

	ecConfig.uiWindowWidth = 800;
	ecConfig.uiWindowHeight = 600;
	ecConfig.bRunInWindowMode = 1;

	AEStackAllocator test(1024*16,"16KAppMainStack");

	AEMemoryConfig::AEMemoryConfigInfo mcInfo( 1024*1024*4, 1024*1024 );
 	InitMemorySystem( &test, mcInfo );
	
	AEngine::Init( &ecConfig );
	glewInit();
	AEFileManager::GetInstance()->AddFileSystem( "my_root", "data" );
	AEFileManager::GetInstance()->AddSearchPath("$my_root/shaders");

	AEFileInStreamI *lpStream = AEFileManager::GetInstance()->Open( argv[1] );
	if( !lpStream )
		return AE_SHADERNOTFOUND;

	AEINT32 nSize = lpStream->GetSize();
	AEUINT8 *lpBuffer = new AEUINT8[nSize+1];

	lpStream->Read( lpBuffer, nSize );

	lpBuffer[nSize] = '\0';
	lpStream->Close();

	GLuint m_uiVSHandle = glCreateShader( GL_VERTEX_SHADER );

	glShaderSource( m_uiVSHandle, 1, (const GLchar**)&lpBuffer, (GLint *)&nSize );

	glCompileShader( m_uiVSHandle );

	GLint nCompileResult;
	glGetShaderiv( m_uiVSHandle, GL_COMPILE_STATUS, &nCompileResult );

	delete [] lpBuffer;

	if( !nCompileResult )
	{
		GLint nMaxLength = 0;
		glGetShaderiv( m_uiVSHandle, GL_INFO_LOG_LENGTH, &nMaxLength );
 
		// The maxLength includes the NULL character
		char *lpErrorLog = new char[nMaxLength];
		glGetShaderInfoLog( m_uiVSHandle, nMaxLength, &nMaxLength, lpErrorLog );

		glDeleteShader( m_uiVSHandle );
		printf("VS: %s\n", lpErrorLog );
		return AE_SHADERCOMPILEERROR;
	}


	lpStream = AEFileManager::GetInstance()->Open( argv[2] );
	if( !lpStream )
		return AE_SHADERNOTFOUND;

	nSize = lpStream->GetSize();
	AEUINT8 *lpBuffer2 = new AEUINT8[nSize+1];

	lpStream->Read( lpBuffer2, nSize );

	lpBuffer2[nSize] = '\0';
	lpStream->Close();

	GLuint m_uiPSHandle = glCreateShader( GL_FRAGMENT_SHADER );

	glShaderSource( m_uiPSHandle, 1, (const GLchar**)&lpBuffer2, (GLint *)&nSize );

	glCompileShader( m_uiPSHandle );

	glGetShaderiv( m_uiPSHandle, GL_COMPILE_STATUS, &nCompileResult );

	delete [] lpBuffer2;

	if( !nCompileResult )
	{
		GLint nMaxLength = 0;
		glGetShaderiv( m_uiPSHandle, GL_INFO_LOG_LENGTH, &nMaxLength );
 
		// The maxLength includes the NULL character
		char *lpErrorLog = new char[nMaxLength];
		glGetShaderInfoLog( m_uiPSHandle, nMaxLength, &nMaxLength, lpErrorLog );

		glDeleteShader( m_uiPSHandle );
		printf("PS: %s\n", lpErrorLog );
		return AE_SHADERCOMPILEERROR;
	}

	const GLuint glProgram = glCreateProgram();

	glAttachShader( glProgram, m_uiVSHandle );
	glAttachShader( glProgram, m_uiPSHandle );
	glLinkProgram( glProgram );
	glGetProgramiv( glProgram, GL_LINK_STATUS, &nCompileResult );
	if( nCompileResult )
	{
		char szName[64];
		AEINT32 nMaxLength = 64;
		AEINT32 nActualLength = 0;
		AEINT32 nCount = 0;
		AEINT32 nLocation = -1;
		GLenum eType;
		AETArray<lpVSAttributes> arrAttributes(1024);

		glGetProgramiv( glProgram, GL_ACTIVE_ATTRIBUTES, (GLint *)&nCount );
		
		for( int i = 0; i < nCount; i++ )
		{
			glGetActiveAttrib( glProgram, i, nMaxLength, (GLint *)&nActualLength, (GLint *)&nSize, &eType, szName );

			if( nSize > 1 )
			{
				for( int j = 0; j < nSize; j++ )
				{
					char szLongName[64];
					sprintf( szLongName, "%s[%i]", szName, j );
					nLocation = glGetAttribLocation( glProgram, szLongName );

					#ifdef AEDEBUG
						printf ("  %i) ATTRIB -> type:%s name:%s location:%i\n", i, GL_type_to_string( eType ), szLongName, nLocation );
					#endif
				}
			}
			else
			{
				nLocation = glGetAttribLocation( glProgram, szName );
				lpVSAttributes lpAttr = new VSAttributes;

				lpAttr->nPos = nLocation;
				lpAttr->nType = eType;
				strncpy( lpAttr->szName, szName, 32 );
				#ifdef AEDEBUG
					printf ("  %i) ATTRIB -> type:%s name:%s location:%d\n", i, GL_type_to_string( eType ), szName, nLocation );
				#endif
			}

			//AERegisterToUniformMappingEntry map;
			//map.nRegister = nLocation;
			//map.uiUniformType =
		}

		glGetProgramiv( glProgram, GL_ACTIVE_UNIFORMS, (GLint *)&nCount );

		for( int i = 0; i < nCount; i++ )
		{
			glGetActiveUniform( glProgram, i, nMaxLength, (GLint *)&nActualLength, (GLint *)&nSize, &eType, szName );
			
			if( nSize > 1 )
			{
				for( int j = 0; j < nSize; j++ )
				{
					char szLongName[64];
					sprintf( szLongName, "%s[%i]", szName, j );
					nLocation = glGetUniformLocation( glProgram, szLongName );
					if( nLocation != GL_INVALID_OPERATION && nLocation != GL_INVALID_VALUE )
					{
						#ifdef AEDEBUG
							printf ("  %i) type:%s name:%s location:%d\n", i, GL_type_to_string( eType ), szLongName, nLocation );
						#endif
					}
				}
			}
			else
			{
				nLocation = glGetUniformLocation( glProgram, szName );
				if( nLocation != GL_INVALID_OPERATION && nLocation != GL_INVALID_VALUE )
				{
					#ifdef AEDEBUG
						printf ("  %i) UNIFORM -> type:%s name:%s location:%d\n", i, GL_type_to_string( eType ), szName, nLocation );
					#endif
				}
			}
		}
	}
	else
	{
		GLint nMaxLength = 0;
		glGetProgramiv( glProgram, GL_INFO_LOG_LENGTH, &nMaxLength );
 
		// The maxLength includes the NULL character
		char *lpErrorLog = new char[nMaxLength];
		glGetProgramInfoLog( glProgram, nMaxLength, &nMaxLength, lpErrorLog );

		glDeleteProgram( glProgram );
		return AE_SHADERLINKERROR;
	}

	glDeleteProgram( glProgram );
	return 0;
}